/* *****************************************************************
 *
 * Download latest Blinker library here:
 * https://github.com/blinker-iot/blinker-library/archive/master.zip
 * 
 * 
 * Blinker is a cross-hardware, cross-platform solution for the IoT. 
 * It provides APP, device and server support, 
 * and uses public cloud services for data transmission and storage.
 * It can be used in smart home, data monitoring and other fields 
 * to help users build Internet of Things projects better and faster.
 * 
 * Make sure installed 2.7.4 or later ESP8266/Arduino package,
 * if use ESP8266 with Blinker.
 * https://github.com/esp8266/Arduino/releases
 * 
 * Make sure installed 1.0.5 or later ESP32/Arduino package,
 * if use ESP32 with Blinker.
 * https://github.com/espressif/arduino-esp32/releases
 * 
 * Docs: https://diandeng.tech/doc
 *       
 * 
 * *****************************************************************
 * 
 * Blinker 库下载地址:
 * https://github.com/blinker-iot/blinker-library/archive/master.zip
 * 
 * Blinker 是一套跨硬件、跨平台的物联网解决方案，提供APP端、设备端、
 * 服务器端支持，使用公有云服务进行数据传输存储。可用于智能家居、
 * 数据监测等领域，可以帮助用户更好更快地搭建物联网项目。
 * 
 * 如果使用 ESP8266 接入 Blinker,
 * 请确保安装了 2.7.4 或更新的 ESP8266/Arduino 支持包。
 * https://github.com/esp8266/Arduino/releases
 * 
 * 如果使用 ESP32 接入 Blinker,
 * 请确保安装了 1.0.5 或更新的 ESP32/Arduino 支持包。
 * https://github.com/espressif/arduino-esp32/releases
 * 
 * 文档: https://diandeng.tech/doc
 *       
 * 
 * *****************************************************************/

/*esp_fan_blinker-0.1----This ia a Blinker esp8266 program control fan to cooler nas PC.
read tempreture compare to fan_run_tem by ds18b20.

*/
#define BLINKER_WIFI

#include <Blinker.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <EEPROM.h>

#define DEBUG 1
//auth 从https://diandeng.tech 注册申请secret key。
char auth[] = "your_blinker_auth";
char ssid[] = "your_wifi_ip";
char pswd[] = "your_wifi_pass";

//hardware pin.
// digital pin 5 has a wifiBtn attached to it. Give it a name:
int wifiBtn = D1;
int pwrCtrl = D6;
int relayCtrl = D5;
#define NET_LED D7

#define FAN_RUN_TEM_DEFAULT_VAL 25.0
float fan_run_tem = FAN_RUN_TEM_DEFAULT_VAL;

enum RUN_MODE {AUTO,MANUAL,OFFLINE,STOP};
int mode = STOP ;

//ds18b20 
#define TEM_DQ_PIN D2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(TEM_DQ_PIN);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
// arrays to hold device address
DeviceAddress Tem_Addr;

float tem_val = 0;
int Fan_flag=0;
uint32_t read_time = 0;

//eeprom
#define BLINKER_EEPROM_SIZE 2447
#define CUSTOM_EEPROM_START (BLINKER_EEPROM_SIZE + 8)

#define SN_MAX_LEN 7
#define NM_MAX_LEN 7
#define PUBDATA_START_ADDR  CUSTOM_EEPROM_START
//public data
typedef struct  {
  char sn[SN_MAX_LEN+1];
  char name[NM_MAX_LEN+1];
  int mode;

}pubConfig;
typedef struct {
  float tem;
  float fan_tem;

}pubVal;

typedef struct {
  pubConfig cfg;
  pubVal val;
} pubData;

typedef union  {
  pubData dat;
  byte mem[sizeof(pubData)];
}memData_t;

memData_t memData;


// 新建组件对象
BlinkerText   TextFanState("tex-fan");
BlinkerText   TextModeState("tex-auto");
BlinkerText   TextSn("tex-sn");
BlinkerText   TextStatus("tex-status");

BlinkerButton Button1("btn-abc");
BlinkerButton Button2("btn-fan");
BlinkerButton Button3("btn-auto");

BlinkerSlider Slider1("ran-set-tem");


BlinkerNumber Number1("num-abc");

//tempreture
BlinkerNumber TEMP("temp");
BlinkerNumber SET_TEMP("set-temp");

int counter = 0;

// 按下按键即会执行该函数
void button1_callback(const String & state)
{
    BLINKER_LOG("get button state: ", state);
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}

void button2_callback(const String & state)
{
    BLINKER_LOG("get button state: ", state);
    if(mode == MANUAL ){
      if (state=="on") {
          digitalWrite(pwrCtrl, HIGH);
          // 反馈开关状态
          Button2.print("on");
          TextFanState.print("run");
      } else if(state=="off"){
          digitalWrite(pwrCtrl, LOW);
          // 反馈开关状态
          Button2.print("off");
          TextFanState.print("stop");

      }

    }else{
      TextStatus.print("fan control by auto.");
      Blinker.delay(1500);
      TextStatus.print("");

    }
    

}

void button3_callback(const String & state)
{
    BLINKER_LOG("get button state: ", state);
    if (state=="on") {
        mode = AUTO;
        // 反馈开关状态
        Button3.print("on");
        TextModeState.print("AUTO");
    } else if(state=="off"){
        mode = MANUAL;

        // 反馈开关状态
        Button3.print("off");
        TextModeState.print("MANUAL");

    }

}
//set tem 
void slider1_callback(int32_t value)
{
    BLINKER_LOG("get slider value: ", value);
    fan_run_tem = value+0.0;
    BLINKER_LOG("fan_run_tem  value: ", fan_run_tem);
    //update 2 eeprom
    memData.dat.val.fan_tem = fan_run_tem;
    pub2eem(&memData);
}

// 如果未绑定的组件被触发，则会执行其中内容
void dataRead(const String & data)
{
    BLINKER_LOG("Blinker readString: ", data);
    counter++;
    Number1.print(counter);
}
void rtData()
{
    Blinker.sendRtData("temp", tem_val);
    Blinker.sendRtData("set-temp", fan_run_tem);
    Blinker.printRtData();
}

//read tempreture.

void heartbeat()
{
  TEMP.print(tem_val);
  SET_TEMP.print(fan_run_tem);
}

void dataStorage()
{
  Blinker.dataStorage("temp", tem_val);
  Blinker.dataStorage("set-temp", fan_run_tem);
}

void testEeprom()
{
  int addr=0;
   EEPROM.begin(4096); //申请操作到地址4095（比如你只需要读写地址为100上的一个字节，该处也需输入参数101）
  char ch=0;  

  for(addr = 0; addr<4096; addr++)
  {
    int data = EEPROM.read(addr); //读数据
    ch=data;
    Serial.print(data);
    if(data==255)
    {
      Serial.print(" ");

    }else{
      Serial.print(" (");
      Serial.print(ch);
      Serial.print(") ");
    }
    Blinker.delay(2);
    if((addr+1)%256 == 0) //每读取256字节数据换行
    {
      Serial.println();
      Serial.print("--[");
      Serial.print(addr);
      Serial.print("]");
    }
  }
  EEPROM.end();
  Serial.println("End read");

}

void printMem(const char *  p,int len)
{

  Serial.print("mem data len:");
  Serial.println(len);
  Serial.println("\n-------------------------");
  Serial.println("      Mem List        ");
  Serial.println("index\taddr\tdata  ");
  Serial.println("-------------------------");


  for(int i=0;i<len;i++)
  {
    Serial.print("  ");
    Serial.print(i+1);
    Serial.print("\t 0x");
    Serial.print(long(p),HEX);
    Serial.print(" \t 0x");
    Serial.print(*p,HEX);
    Serial.print(" (");
    Serial.print(*p);
    Serial.println(")");
    if((i+1)%8==0)
      Serial.println("      .............      ");
    p++;


  }
  Serial.println("-------------------------");

}

//save public data to e2prom

int pub2eem( memData_t *pdat)
{

  EEPROM.begin(4096); //申请操作到地址4095（比如你只需要读写地址为100上的一个字节，该处也需输入参数101）
  int len=sizeof(pdat->mem);
  Serial.print("save ");
  Serial.print(len);
  Serial.println(" bytes to e2prom.");
  
  for(int i=0;i<len;i++ )
  {
    EEPROM.write(PUBDATA_START_ADDR+i,pdat->mem[i]);
  }

  if (EEPROM.commit()) {
      Serial.println("EEPROM successfully committed");
    } else {
      Serial.println("ERROR! EEPROM commit failed");
    }

    EEPROM.end();

  
  return 0;
}
int eem2pub(memData_t *pdat)
{
  EEPROM.begin(4096); //申请操作到地址4095（比如你只需要读写地址为100上的一个字节，该处也需输入参数101）
  int len=sizeof(pdat->mem);
  Serial.print("read ");
  Serial.print(len);
  Serial.println(" bytes from e2prom.");
  for(int i=0;i<len;i++ )
		{

		pdat->mem[i]=EEPROM.read(PUBDATA_START_ADDR+i);
		}

  EEPROM.end();
  return 0;
}

char printPubConfig(memData_t *pdat){

		//ele val infor list
	Serial.println("------pubConfig Debug-----");
	Serial.print("name:");
	Serial.println(pdat->dat.cfg.name);
	Serial.print("sn:");
	Serial.println(pdat->dat.cfg.sn);
	Serial.print("mode:");
	Serial.println(pdat->dat.cfg.mode);
	Serial.print("fan tem:");
	Serial.println(pdat->dat.val.fan_tem);
	Serial.println("--------------------------");

	return 0;

}

uint8_t fan_flag = 0;

void setup()
{
    // 初始化串口
    Serial.begin(115200);
    Serial.println("\nproj:esp_fan_blinker-0.1.");
    //BLINKER_DEBUG.stream(Serial);
    //BLINKER_DEBUG.debugAll();
    
    // 初始化有LED的IO
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);
    pinMode(NET_LED, OUTPUT);  
    pinMode(wifiBtn, INPUT_PULLUP);
    pinMode(pwrCtrl, OUTPUT);
//ini 
    digitalWrite(pwrCtrl, LOW);
    digitalWrite(NET_LED, HIGH);  // Turn the LED off (Note that LOW is the voltage level

   //testEeprom();
   //update pubcfg from eeprom.
    eem2pub(&memData);
    printPubConfig(&memData);
    mode = memData.dat.cfg.mode;
    fan_run_tem = memData.dat.val.fan_tem;

    // 初始化blinker
    Blinker.begin(auth, ssid, pswd);
    Blinker.attachData(dataRead);

    Button1.attach(button1_callback);
    Button2.attach(button2_callback);
    Button3.attach(button3_callback);

    Slider1.attach(slider1_callback);

    Blinker.attachHeartbeat(heartbeat);
    Blinker.attachRTData(rtData);

    Blinker.attachDataStorage(dataStorage);
    Slider1.print(fan_run_tem);
    TextSn.print(memData.dat.cfg.sn);
     //ds1820
    Serial.println("init ds18b20.");
    iniDs18b20(Tem_Addr);
    Serial.print("Ds18b20 Addr:");
    printAddress(Tem_Addr);
}

void loop() {
    Blinker.run();
    
    if (read_time == 0 || (millis() - read_time) >= 2000)
    {
        read_time = millis();
        
        float t = getTemperature(Tem_Addr);        

        if (isnan(t)) {
            BLINKER_LOG("Failed to read from DS18b20 sensor!");
            return;
        }

        tem_val = t;

        BLINKER_LOG("Temperature: ", t, " *C");
    }

    if(mode == AUTO)
    { 
      
      if( (tem_val - fan_run_tem > 0.01) ){
        Blinker.delay(1000);
        if( (tem_val - fan_run_tem > 0.01) ){
          fan_flag = 1;
          Serial.println("open fan.");
        }

        
      } else {
        fan_flag = 0;
      }

      if(fan_flag == 1 )
        {
          //relay action,open fan
          digitalWrite(pwrCtrl, HIGH);
          // 反馈开关状态
          Button2.print("on");
          TextFanState.print("run");

        }else{
          digitalWrite(pwrCtrl, LOW);
          // 反馈开关状态
          Button2.print("off");
          TextFanState.print("stop"); 

        }
    }
    
}

uint8_t iniDs18b20(DeviceAddress& addr) {
  int count = 0;
  // start serial port
  if (DEBUG) Serial.begin(115200);

  // locate devices on the bus
  sensors.begin();
  count = sensors.getDeviceCount();
  if (DEBUG) {
    Serial.print("Found ");
    Serial.print(count, DEC);
    Serial.println(" devices.");
  }




  if (!sensors.getAddress(addr, 0)) {

    if (DEBUG) Serial.println("Unable to find address for Device 0");
    return 0;
  } else {
    if (DEBUG) {
      Serial.print("Device 0 Address: ");
      printAddress(addr);
      Serial.println();
    }
    // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
    sensors.setResolution(addr, 9);
  }
  return count;
}
// function to print a device address
void printAddress(DeviceAddress deviceAddress) {
  for (uint8_t i = 0; i < 8; i++) {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
  Serial.println();
}

float getTemperature(DeviceAddress& addr) {
  if (DEBUG) Serial.print("Requesting temperatures...");
  sensors.requestTemperatures();  // Send the command to get temperatures
  if (DEBUG) Serial.println("DONE");

  float tempC = sensors.getTempC(addr);
  if (DEBUG) Serial.print("Temp C: ");
  if (DEBUG) Serial.print(tempC);
  Serial.println();
  return tempC;
}
